home *** CD-ROM | disk | FTP | other *** search
/ Freelog 70 / Freelog070.iso / Internet / EasyPHP / easyphp1-8_setup.exe / {app} / phpmyadmin / libraries / sqlparser.lib.php < prev    next >
Encoding:
PHP Script  |  2004-11-04  |  82.8 KB  |  2,145 lines

  1. <?php
  2. /* $Id: sqlparser.lib.php,v 2.27 2004/11/05 00:41:55 lem9 Exp $ */
  3. // vim: expandtab sw=4 ts=4 sts=4:
  4.  
  5. /** SQL Parser Functions for phpMyAdmin
  6.  *
  7.  * Copyright 2002 Robin Johnson <robbat2@users.sourceforge.net>
  8.  * http://www.orbis-terrarum.net/?l=people.robbat2
  9.  *
  10.  * These functions define an SQL parser system, capable of understanding and
  11.  * extracting data from a MySQL type SQL query.
  12.  *
  13.  * The basic procedure for using the new SQL parser:
  14.  * On any page that needs to extract data from a query or to pretty-print a
  15.  * query, you need code like this up at the top:
  16.  *
  17.  * ($sql contains the query)
  18.  * $parsed_sql = PMA_SQP_parse($sql);
  19.  *
  20.  * If you want to extract data from it then, you just need to run
  21.  * $sql_info = PMA_SQP_analyze($parsed_sql);
  22.  *
  23.  * lem9: See comments in PMA_SQP_analyze for the returned info
  24.  *       from the analyzer.
  25.  *
  26.  * If you want a pretty-printed version of the query, do:
  27.  * $string = PMA_SQP_formatHtml($parsed_sql);
  28.  * (note that that you need to have syntax.css.php included somehow in your
  29.  * page for it to work, I recommend '<link rel="stylesheet" type="text/css"
  30.  * href="syntax.css.php" />' at the moment.)
  31.  */
  32.  
  33.  
  34. /**
  35.  * Minimum inclusion? (i.e. for the stylesheet builder)
  36.  */
  37.  
  38. if (!isset($is_minimum_common)) {
  39.     $is_minimum_common = FALSE;
  40. }
  41.  
  42. if ($is_minimum_common == FALSE) {
  43.     /**
  44.      * Include the string library as we use it heavily
  45.      */
  46.     require_once('./libraries/string.lib.php');
  47.  
  48.     /**
  49.      * Include data for the SQL Parser
  50.      */
  51.     require_once('./libraries/sqlparser.data.php');
  52.     require_once('./libraries/mysql_charsets.lib.php');
  53.     if (!isset($mysql_charsets)) {
  54.         $mysql_charsets = array();
  55.         $mysql_charsets_count = 0;
  56.         $mysql_collations_flat = array();
  57.         $mysql_collations_count = 0;
  58.     }
  59.  
  60.     if (!defined('DEBUG_TIMING')) {
  61.         function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize)
  62.         {
  63.             $arr[] = array('type' => $type, 'data' => $data);
  64.             $arrsize++;
  65.         } // end of the "PMA_SQP_arrayAdd()" function
  66.     } else {
  67.         function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize)
  68.         {
  69.             global $timer;
  70.  
  71.             $t     = $timer;
  72.             $arr[] = array('type' => $type, 'data' => $data , 'time' => $t);
  73.             $timer = microtime();
  74.             $arrsize++;
  75.         } // end of the "PMA_SQP_arrayAdd()" function
  76.     } // end if... else...
  77.  
  78.  
  79.     /**
  80.      * Reset the error variable for the SQL parser
  81.      *
  82.      * @access public
  83.      */
  84.     // Added, Robbat2 - 13 Janurary 2003, 2:59PM
  85.     function PMA_SQP_resetError() {
  86.         global $SQP_errorString;
  87.         $SQP_errorString = '';
  88.         unset($SQP_errorString);
  89.     }
  90.  
  91.     /**
  92.      * Get the contents of the error variable for the SQL parser
  93.      *
  94.      * @return string Error string from SQL parser
  95.      *
  96.      * @access public
  97.      */
  98.     // Added, Robbat2 - 13 Janurary 2003, 2:59PM
  99.     function PMA_SQP_getErrorString() {
  100.         global $SQP_errorString;
  101.         return isset($SQP_errorString) ? $SQP_errorString : '';
  102.     }
  103.  
  104.     /**
  105.      * Check if the SQL parser hit an error
  106.      *
  107.      * @return boolean error state
  108.      *
  109.      * @access public
  110.      */
  111.     // Added, Robbat2 - 13 Janurary 2003, 2:59PM
  112.     function PMA_SQP_isError() {
  113.         global $SQP_errorString;
  114.         return isset($SQP_errorString) && !empty($SQP_errorString);
  115.     }
  116.  
  117.     /**
  118.      * Set an error message for the system
  119.      *
  120.      * @param  string  The error message
  121.      * @param  string  The failing SQL query
  122.      *
  123.      * @access private
  124.      * @scope SQL Parser internal
  125.      */
  126.     // Revised, Robbat2 - 13 Janurary 2003, 2:59PM
  127.     function PMA_SQP_throwError($message, $sql)
  128.     {
  129.  
  130.         global $SQP_errorString;
  131.         $SQP_errorString = '<p>'.$GLOBALS['strSQLParserUserError'] . '</p>' . "\n"
  132.             . '<pre>' . "\n"
  133.             . 'ERROR: ' . $message . "\n"
  134.             . 'SQL: ' . htmlspecialchars($sql) .  "\n"
  135.             . '</pre>' . "\n";
  136.  
  137.     } // end of the "PMA_SQP_throwError()" function
  138.  
  139.  
  140.     /**
  141.      * Do display the bug report
  142.      *
  143.      * @param  string  The error message
  144.      * @param  string  The failing SQL query
  145.      *
  146.      * @access public
  147.      */
  148.     function PMA_SQP_bug($message, $sql)
  149.     {
  150.         global $SQP_errorString;
  151.         $debugstr = 'ERROR: ' . $message . "\n";
  152.         $debugstr .= 'CVS: $Id: sqlparser.lib.php,v 2.27 2004/11/05 00:41:55 lem9 Exp $' . "\n";
  153.         $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION . "\n";
  154.         $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS . ' ' . PMA_USR_BROWSER_AGENT . ' ' . PMA_USR_BROWSER_VER . "\n";
  155.         $debugstr .= 'PMA: ' . PMA_VERSION . "\n";
  156.         $debugstr .= 'PHP VER,OS: ' . PMA_PHP_STR_VERSION . ' ' . PHP_OS . "\n";
  157.         $debugstr .= 'LANG: ' . $GLOBALS['lang'] . "\n";
  158.         $debugstr .= 'SQL: ' . htmlspecialchars($sql);
  159.  
  160.         $encodedstr     = $debugstr;
  161.         if (@function_exists('gzcompress')) {
  162.             $encodedstr = gzcompress($debugstr, 9);
  163.         }
  164.         $encodedstr     = preg_replace("/(\015\012)|(\015)|(\012)/", '<br />' . "\n", chunk_split(base64_encode($encodedstr)));
  165.  
  166.         $SQP_errorString .= $GLOBALS['strSQLParserBugMessage'] . '<br />' . "\n"
  167.              . '----' . $GLOBALS['strBeginCut'] . '----' . '<br />' . "\n"
  168.              . $encodedstr . "\n"
  169.              . '----' . $GLOBALS['strEndCut'] . '----' . '<br />' . "\n";
  170.  
  171.         $SQP_errorString .= '----' . $GLOBALS['strBeginRaw'] . '----<br />' . "\n"
  172.              . '<pre>' . "\n"
  173.              . $debugstr
  174.              . '</pre>' . "\n"
  175.              . '----' . $GLOBALS['strEndRaw'] . '----<br />' . "\n";
  176.  
  177.     } // end of the "PMA_SQP_bug()" function
  178.  
  179.  
  180.     /**
  181.      * Parses the SQL queries
  182.      *
  183.      * @param  string   The SQL query list
  184.      *
  185.      * @return mixed    Most of times, nothing...
  186.      *
  187.      * @global array    The current PMA configuration
  188.      * @global array    MySQL column attributes
  189.      * @global array    MySQL reserved words
  190.      * @global array    MySQL column types
  191.      * @global array    MySQL function names
  192.      * @global integer  MySQL column attributes count
  193.      * @global integer  MySQL reserved words count
  194.      * @global integer  MySQL column types count
  195.      * @global integer  MySQL function names count
  196.      * @global array    List of available character sets
  197.      * @global array    List of available collations
  198.      * @global integer  Character sets count
  199.      * @global integer  Collations count
  200.      *
  201.      * @access public
  202.      */
  203.     function PMA_SQP_parse($sql)
  204.     {
  205.         global $cfg;
  206.         global $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word, $PMA_SQPdata_column_type, $PMA_SQPdata_function_name,
  207.                $PMA_SQPdata_column_attrib_cnt, $PMA_SQPdata_reserved_word_cnt, $PMA_SQPdata_column_type_cnt, $PMA_SQPdata_function_name_cnt;
  208.         global $mysql_charsets, $mysql_collations_flat, $mysql_charsets_count, $mysql_collations_count;
  209.  
  210.         // rabus: Convert all line feeds to Unix style
  211.         $sql = str_replace("\r\n", "\n", $sql);
  212.         $sql = str_replace("\r", "\n", $sql);
  213.  
  214.         $len = PMA_strlen($sql);
  215.         if ($len == 0) {
  216.             return array();
  217.         }
  218.  
  219.         $sql_array               = array();
  220.         $sql_array['raw']        = $sql;
  221.         $count1                  = 0;
  222.         $count2                  = 0;
  223.         $punct_queryend          = ';';
  224.         $punct_qualifier         = '.';
  225.         $punct_listsep           = ',';
  226.         $punct_level_plus        = '(';
  227.         $punct_level_minus       = ')';
  228.         $digit_floatdecimal      = '.';
  229.         $digit_hexset            = 'x';
  230.         $bracket_list            = '()[]{}';
  231.         $allpunct_list           =  '-,;:!?/.^~\*&%+<=>|';
  232.         $allpunct_list_pair      = array (
  233.             0 => '!=',
  234.             1 => '&&',
  235.             2 => ':=',
  236.             3 => '<<',
  237.             4 => '<=',
  238.             5 => '<=>',
  239.             6 => '<>',
  240.             7 => '>=',
  241.             8 => '>>',
  242.             9 => '||'
  243.         );
  244.         $allpunct_list_pair_size = 10; //count($allpunct_list_pair);
  245.         $quote_list              = '\'"`';
  246.         $arraysize               = 0;
  247.  
  248.         while ($count2 < $len) {
  249.             $c      = PMA_substr($sql, $count2, 1);
  250.             $count1 = $count2;
  251.  
  252.             if (($c == "\n")) {
  253.                 $count2++;
  254.                 PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize);
  255.                 continue;
  256.             }
  257.  
  258.             // Checks for white space
  259.             if (PMA_STR_isSpace($c)) {
  260.                 $count2++;
  261.                 continue;
  262.             }
  263.  
  264.             // Checks for comment lines.
  265.             // MySQL style #
  266.             // C style /* */
  267.             // ANSI style --
  268.             if (($c == '#')
  269.                 || (($count2 + 1 < $len) && ($c == '/') && (PMA_substr($sql, $count2 + 1, 1) == '*'))
  270.                 || (($count2 + 2 == $len) && ($c == '-') && (PMA_substr($sql, $count2 + 1, 1) == '-'))
  271.                 || (($count2 + 2 < $len) && ($c == '-') && (PMA_substr($sql, $count2 + 1, 1) == '-') && ((PMA_substr($sql, $count2 + 2, 1) <= ' ')))) {
  272.                 $count2++;
  273.                 $pos  = 0;
  274.                 $type = 'bad';
  275.                 switch ($c) {
  276.                     case '#':
  277.                         $type = 'mysql';
  278.                     case '-':
  279.                         $type = 'ansi';
  280.                         $pos  = $GLOBALS['PMA_strpos']($sql, "\n", $count2);
  281.                         break;
  282.                     case '/':
  283.                         $type = 'c';
  284.                         $pos  = $GLOBALS['PMA_strpos']($sql, '*/', $count2);
  285.                         $pos  += 2;
  286.                         break;
  287.                     default:
  288.                         break;
  289.                 } // end switch
  290.                 $count2 = ($pos < $count2) ? $len : $pos;
  291.                 $str    = PMA_substr($sql, $count1, $count2 - $count1);
  292.                 PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize);
  293.                 continue;
  294.             } // end if
  295.  
  296.             // Checks for something inside quotation marks
  297.             if (PMA_STR_strInStr($c, $quote_list)) {
  298.                 $startquotepos   = $count2;
  299.                 $quotetype       = $c;
  300.                 $count2++;
  301.                 $escaped         = FALSE;
  302.                 $escaped_escaped = FALSE;
  303.                 $pos             = $count2;
  304.                 $oldpos          = 0;
  305.                 do {
  306.                     $oldpos = $pos;
  307.                     $pos    = $GLOBALS['PMA_strpos'](' ' . $sql, $quotetype, $oldpos + 1) - 1;
  308.                     // ($pos === FALSE)
  309.                     if ($pos < 0) {
  310.                         $debugstr = $GLOBALS['strSQPBugUnclosedQuote'] . ' @ ' . $startquotepos. "\n"
  311.                                   . 'STR: ' . htmlspecialchars($quotetype);
  312.                         PMA_SQP_throwError($debugstr, $sql);
  313.                         return $sql;
  314.                     }
  315.  
  316.                     // If the quote is the first character, it can't be
  317.                     // escaped, so don't do the rest of the code
  318.                     if ($pos == 0) {
  319.                         break;
  320.                     }
  321.  
  322.                     // Checks for MySQL escaping using a \
  323.                     // And checks for ANSI escaping using the $quotetype character
  324.                     if (($pos < $len) && PMA_STR_charIsEscaped($sql, $pos)) {
  325.                         $pos ++;
  326.                         continue;
  327.                     } else if (($pos + 1 < $len) && (PMA_substr($sql, $pos, 1) == $quotetype) && (PMA_substr($sql, $pos + 1, 1) == $quotetype)) {
  328.                         $pos = $pos + 2;
  329.                         continue;
  330.                     } else {
  331.                         break;
  332.                     }
  333.                 } while ($len > $pos); // end do
  334.  
  335.                 $count2       = $pos;
  336.                 $count2++;
  337.                 $type         = 'quote_';
  338.                 switch ($quotetype) {
  339.                     case '\'':
  340.                         $type .= 'single';
  341.                         break;
  342.                     case '"':
  343.                         $type .= 'double';
  344.                         break;
  345.                     case '`':
  346.                         $type .= 'backtick';
  347.                         break;
  348.                     default:
  349.                         break;
  350.                 } // end switch
  351.                 $data = PMA_substr($sql, $count1, $count2 - $count1);
  352.                 PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize);
  353.                 continue;
  354.             }
  355.  
  356.             // Checks for brackets
  357.             if (PMA_STR_strInStr($c, $bracket_list)) {
  358.                 // All bracket tokens are only one item long
  359.                 $count2++;
  360.                 $type_type     = '';
  361.                 if (PMA_STR_strInStr($c, '([{')) {
  362.                     $type_type = 'open';
  363.                 } else {
  364.                     $type_type = 'close';
  365.                 }
  366.  
  367.                 $type_style     = '';
  368.                 if (PMA_STR_strInStr($c, '()')) {
  369.                     $type_style = 'round';
  370.                 } elseif (PMA_STR_strInStr($c, '[]')) {
  371.                     $type_style = 'square';
  372.                 } else {
  373.                     $type_style = 'curly';
  374.                 }
  375.  
  376.                 $type = 'punct_bracket_' . $type_type . '_' . $type_style;
  377.                 PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize);
  378.                 continue;
  379.             }
  380.  
  381.             // Checks for punct
  382.             if (PMA_STR_strInStr($c, $allpunct_list)) {
  383.                 while (($count2 < $len) && PMA_STR_strInStr(PMA_substr($sql, $count2, 1), $allpunct_list)) {
  384.                     $count2++;
  385.                 }
  386.                 $l = $count2 - $count1;
  387.                 if ($l == 1) {
  388.                     $punct_data = $c;
  389.                 } else {
  390.                     $punct_data = PMA_substr($sql, $count1, $l);
  391.                 }
  392.  
  393.                 // Special case, sometimes, althought two characters are
  394.                 // adjectent directly, they ACTUALLY need to be seperate
  395.                 if ($l == 1) {
  396.                     $t_suffix         = '';
  397.                     switch ($punct_data) {
  398.                         case $punct_queryend:
  399.                             $t_suffix = '_queryend';
  400.                             break;
  401.                         case $punct_qualifier:
  402.                             $t_suffix = '_qualifier';
  403.                             break;
  404.                         case $punct_listsep:
  405.                             $t_suffix = '_listsep';
  406.                             break;
  407.                         default:
  408.                             break;
  409.                     }
  410.                     PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize);
  411.                 }
  412.                 else if (PMA_STR_binarySearchInArr($punct_data, $allpunct_list_pair, $allpunct_list_pair_size)) {
  413.                     // Ok, we have one of the valid combined punct expressions
  414.                     PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
  415.                 }
  416.                 else {
  417.                     // Bad luck, lets split it up more
  418.                     $first  = $punct_data[0];
  419.                     $first2 = $punct_data[0] . $punct_data[1];
  420.                     $last2  = $punct_data[$l - 2] . $punct_data[$l - 1];
  421.                     $last   = $punct_data[$l - 1];
  422.                     if (($first == ',') || ($first == ';') || ($first == '.') || ($first == '*')) {
  423.                         $count2     = $count1 + 1;
  424.                         $punct_data = $first;
  425.                     } else if (($last2 == '/*') || (($last2 == '--') && ($count2 == $len || PMA_substr($sql, $count2, 1) <= ' ') )) {
  426.                         $count2     -= 2;
  427.                         $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
  428.                     } else if (($last == '-') || ($last == '+') || ($last == '!')) {
  429.                         $count2--;
  430.                         $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
  431.                     // TODO: for negation operator, split in 2 tokens ?
  432.                     // "select x&~1 from t"
  433.                     // becomes "select x & ~ 1 from t" ?
  434.  
  435.                     } else if ($last != '~') {
  436.                         $debugstr =  $GLOBALS['strSQPBugUnknownPunctuation'] . ' @ ' . ($count1+1) . "\n"
  437.                                   . 'STR: ' . htmlspecialchars($punct_data);
  438.                         PMA_SQP_throwError($debugstr, $sql);
  439.                         return $sql;
  440.                     }
  441.                     PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
  442.                     continue;
  443.                 } // end if... else if... else
  444.                 continue;
  445.             }
  446.  
  447.             // Checks for alpha
  448.             if (PMA_STR_isSqlIdentifier($c, FALSE) || ($c == '@')) {
  449.                 $count2 ++;
  450.  
  451.                 //TODO: a @ can also be present in expressions like
  452.                 // FROM 'user'@'%'
  453.                 // or  TO 'user'@'%'
  454.                 // in this case, the @ is wrongly marked as alpha_variable
  455.  
  456.                 $is_sql_variable         = ($c == '@');
  457.                 $is_digit                = (!$is_sql_variable) && PMA_STR_isDigit($c);
  458.                 $is_hex_digit            = ($is_digit) && ($c == '0') && ($count2 < $len) && (PMA_substr($sql, $count2, 1) == 'x');
  459.                 $is_float_digit          = FALSE;
  460.                 $is_float_digit_exponent = FALSE;
  461.  
  462.                 if ($is_hex_digit) {
  463.                     $count2++;
  464.                 }
  465.  
  466.                 while (($count2 < $len) && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), ($is_sql_variable || $is_digit))) {
  467.                     $c2 = PMA_substr($sql, $count2, 1);
  468.                     if ($is_sql_variable && ($c2 == '.')) {
  469.                         $count2++;
  470.                         continue;
  471.                     }
  472.                     if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) {
  473.                         $count2++;
  474.                         if (!$is_float_digit) {
  475.                             $is_float_digit = TRUE;
  476.                             continue;
  477.                         } else {
  478.                             $debugstr = $GLOBALS['strSQPBugInvalidIdentifer'] . ' @ ' . ($count1+1) . "\n"
  479.                                       . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1));
  480.                             PMA_SQP_throwError($debugstr, $sql);
  481.                             return $sql;
  482.                         }
  483.                     }
  484.                     if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') || ($c2 == 'E'))) {
  485.                         if (!$is_float_digit_exponent) {
  486.                             $is_float_digit_exponent = TRUE;
  487.                             $is_float_digit          = TRUE;
  488.                             $count2++;
  489.                             continue;
  490.                         } else {
  491.                             $is_digit                = FALSE;
  492.                             $is_float_digit          = FALSE;
  493.                         }
  494.                     }
  495.                     if (($is_hex_digit && PMA_STR_isHexDigit($c2)) || ($is_digit && PMA_STR_isDigit($c2))) {
  496.                         $count2++;
  497.                         continue;
  498.                     } else {
  499.                         $is_digit     = FALSE;
  500.                         $is_hex_digit = FALSE;
  501.                     }
  502.  
  503.                     $count2++;
  504.                 } // end while
  505.  
  506.                 $l    = $count2 - $count1;
  507.                 $str  = PMA_substr($sql, $count1, $l);
  508.  
  509.                 $type = '';
  510.                 if ($is_digit) {
  511.                     $type     = 'digit';
  512.                     if ($is_float_digit) {
  513.                         $type .= '_float';
  514.                     } else if ($is_hex_digit) {
  515.                         $type .= '_hex';
  516.                     } else {
  517.                         $type .= '_integer';
  518.                     }
  519.                 }
  520.                 else {
  521.                     if ($is_sql_variable != FALSE) {
  522.                         $type = 'alpha_variable';
  523.                     } else {
  524.                         $type = 'alpha';
  525.                     }
  526.                 } // end if... else....
  527.                 PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize);
  528.  
  529.                 continue;
  530.             }
  531.  
  532.             // DEBUG
  533.             $count2++;
  534.  
  535.             $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len .  "\n"
  536.                       . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n";
  537.             PMA_SQP_bug($debugstr, $sql);
  538.             return $sql;
  539.  
  540.         } // end while ($count2 < $len)
  541.  
  542.  
  543.         if ($arraysize > 0) {
  544.           $t_next           = $sql_array[0]['type'];
  545.           $t_prev           = '';
  546.           $t_bef_prev       = '';
  547.           $t_cur            = '';
  548.           $d_next           = $sql_array[0]['data'];
  549.           $d_prev           = '';
  550.           $d_bef_prev       = '';
  551.           $d_cur            = '';
  552.           $d_next_upper     = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
  553.           $d_prev_upper     = '';
  554.           $d_bef_prev_upper = '';
  555.           $d_cur_upper      = '';
  556.         }
  557.  
  558.         for ($i = 0; $i < $arraysize; $i++) {
  559.           $t_bef_prev       = $t_prev;
  560.           $t_prev           = $t_cur;
  561.           $t_cur            = $t_next;
  562.           $d_bef_prev       = $d_prev;
  563.           $d_prev           = $d_cur;
  564.           $d_cur            = $d_next;
  565.           $d_bef_prev_upper = $d_prev_upper;
  566.           $d_prev_upper     = $d_cur_upper;
  567.           $d_cur_upper      = $d_next_upper;
  568.           if (($i + 1) < $arraysize) {
  569.             $t_next = $sql_array[$i + 1]['type'];
  570.             $d_next = $sql_array[$i + 1]['data'];
  571.             $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
  572.           } else {
  573.             $t_next       = '';
  574.             $d_next       = '';
  575.             $d_next_upper = '';
  576.           }
  577.  
  578.           //DEBUG echo "[prev: <b>".$d_prev."</b> ".$t_prev."][cur: <b>".$d_cur."</b> ".$t_cur."][next: <b>".$d_next."</b> ".$t_next."]<br />";
  579.  
  580.           if ($t_cur == 'alpha') {
  581.             $t_suffix     = '_identifier';
  582.             if (($t_next == 'punct_qualifier') || ($t_prev == 'punct_qualifier')) {
  583.               $t_suffix = '_identifier';
  584.             } else if (($t_next == 'punct_bracket_open_round')
  585.             && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_function_name, $PMA_SQPdata_function_name_cnt)) {
  586.               $t_suffix = '_functionName';
  587.             } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt))  {
  588.               $t_suffix = '_columnType';
  589.  
  590.               // Temporary fix for BUG #621357
  591.               //TODO FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER
  592.               if ($d_cur_upper == 'SET' && $t_next != 'punct_bracket_open_round') {
  593.                 $t_suffix = '_reservedWord';
  594.               }
  595.               //END OF TEMPORARY FIX
  596.  
  597.               // CHARACTER is a synonym for CHAR, but can also be meant as
  598.               // CHARACTER SET. In this case, we have a reserved word.
  599.               if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') {
  600.                 $t_suffix = '_reservedWord';
  601.               }
  602.  
  603.               // experimental
  604.               // current is a column type, so previous must not be
  605.               // a reserved word but an identifier
  606.               // CREATE TABLE SG_Persons (first varchar(64))
  607.  
  608.               //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') {
  609.               //    $sql_array[$i-1]['type'] = 'alpha_identifier';
  610.               //}
  611.  
  612.             } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_reserved_word, $PMA_SQPdata_reserved_word_cnt)) {
  613.               $t_suffix = '_reservedWord';
  614.             } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_attrib, $PMA_SQPdata_column_attrib_cnt)) {
  615.               $t_suffix = '_columnAttrib';
  616.               // INNODB is a MySQL table type, but in "SHOW INNODB STATUS",
  617.               // it should be regarded as a reserved word.
  618.               if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') {
  619.                 $t_suffix = '_reservedWord';
  620.               }
  621.  
  622.               if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') {
  623.                 $t_suffix = '_reservedWord';
  624.               }
  625.               // Binary as character set
  626.               if ($d_cur_upper == 'BINARY' && (
  627.                   ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET')
  628.                   || ($d_bef_prev_upper == 'SET' && $d_prev_upper == '=')
  629.                   || ($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=')
  630.                   || $d_prev_upper == 'CHARSET'
  631.                   ) && PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, count($mysql_charsets))) {
  632.                   $t_suffix = '_charset';
  633.               }
  634.             } elseif (PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, $mysql_charsets_count)
  635.               || PMA_STR_binarySearchInArr($d_cur, $mysql_collations_flat, $mysql_collations_count)
  636.               || ($d_cur{0} == '_' && PMA_STR_binarySearchInArr(substr($d_cur, 1), $mysql_charsets, $mysql_charsets_count))) {
  637.               $t_suffix = '_charset';
  638.             } else {
  639.               // Do nothing
  640.             }
  641.             $sql_array[$i]['type'] .= $t_suffix;
  642.           }
  643.         } // end for
  644.  
  645.         // Stores the size of the array inside the array, as count() is a slow
  646.         // operation.
  647.         $sql_array['len'] = $arraysize;
  648.  
  649.         // Sends the data back
  650.         return $sql_array;
  651.     } // end of the "PMA_SQP_parse()" function
  652.  
  653.    /**
  654.     * Checks for token types being what we want...
  655.     *
  656.     * @param  string String of type that we have
  657.     * @param  string String of type that we want
  658.     *
  659.     * @return boolean result of check
  660.     *
  661.     * @access private
  662.     */
  663.     function PMA_SQP_typeCheck($toCheck, $whatWeWant)
  664.     {
  665.         $typeSeperator = '_';
  666.         if (strcmp($whatWeWant, $toCheck) == 0) {
  667.             return TRUE;
  668.         } else {
  669.             if (strpos($whatWeWant, $typeSeperator) === FALSE) {
  670.                 return strncmp($whatWeWant, $toCheck , strpos($toCheck, $typeSeperator)) == 0;
  671.             } else {
  672.                 return FALSE;
  673.             }
  674.         }
  675.     }
  676.  
  677.  
  678.     /**
  679.      * Analyzes SQL queries
  680.      *
  681.      * @param  array   The SQL queries
  682.      *
  683.      * @return array   The analyzed SQL queries
  684.      *
  685.      * @access public
  686.      */
  687.     function PMA_SQP_analyze($arr)
  688.     {
  689.         $result          = array();
  690.         $size            = $arr['len'];
  691.         $subresult       = array(
  692.             'querytype'      => '',
  693.             'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT
  694.             'position_of_first_select' => '', // the array index
  695.             'from_clause'=> '',
  696.             'group_by_clause'=> '',
  697.             'order_by_clause'=> '',
  698.             'having_clause'  => '',
  699.             'where_clause'   => '',
  700.             'where_clause_identifiers'   => array(),
  701.             'unsorted_query' => '',
  702.             'queryflags'     => array(),
  703.             'select_expr'    => array(),
  704.             'table_ref'      => array(),
  705.             'foreign_keys'   => array()
  706.         );
  707.         $subresult_empty = $subresult;
  708.         $seek_queryend         = FALSE;
  709.         $seen_end_of_table_ref = FALSE;
  710.  
  711.         // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE())
  712.         // we must not use CURDATE as a table_ref
  713.         // so we track wether we are in the EXTRACT()
  714.         $in_extract          = FALSE;
  715.  
  716.         // for GROUP_CONCAT( ... )
  717.         $in_group_concat     = FALSE;
  718.  
  719. /* Description of analyzer results
  720.  *
  721.  * lem9: db, table, column, alias
  722.  *      ------------------------
  723.  *
  724.  * Inside the $subresult array, we create ['select_expr'] and ['table_ref'] arrays.
  725.  *
  726.  * The SELECT syntax (simplified) is
  727.  *
  728.  * SELECT
  729.  *    select_expression,...
  730.  *    [FROM [table_references]
  731.  *
  732.  *
  733.  * ['select_expr'] is filled with each expression, the key represents the
  734.  * expression position in the list (0-based) (so we don't lose track of
  735.  * multiple occurences of the same column).
  736.  *
  737.  * ['table_ref'] is filled with each table ref, same thing for the key.
  738.  *
  739.  * I create all sub-values empty, even if they are
  740.  * not present (for example no select_expression alias).
  741.  *
  742.  * There is a debug section at the end of loop #1, if you want to
  743.  * see the exact contents of select_expr and table_ref
  744.  *
  745.  * lem9: queryflags
  746.  *       ----------
  747.  *
  748.  * In $subresult, array 'queryflags' is filled, according to what we
  749.  * find in the query.
  750.  *
  751.  * Currently, those are generated:
  752.  *
  753.  * ['queryflags']['need_confirm'] = 1; if the query needs confirmation
  754.  * ['queryflags']['select_from'] = 1;  if this is a real SELECT...FROM
  755.  * ['queryflags']['distinct'] = 1;     for a DISTINCT
  756.  * ['queryflags']['union'] = 1;        for a UNION
  757.  * ['queryflags']['join'] = 1;         for a JOIN
  758.  * ['queryflags']['offset'] = 1;       for the presence of OFFSET 
  759.  *
  760.  * lem9:  query clauses
  761.  *        -------------
  762.  *
  763.  * The select is splitted in those clauses:
  764.  * ['select_expr_clause']
  765.  * ['from_clause']
  766.  * ['group_by_clause']
  767.  * ['order_by_clause']
  768.  * ['having_clause']
  769.  * ['where_clause']
  770.  *
  771.  * The identifiers of the WHERE clause are put into the array
  772.  * ['where_clause_identifier']
  773.  *
  774.  * For a SELECT, the whole query without the ORDER BY clause is put into
  775.  * ['unsorted_query']
  776.  *
  777.  * lem9:   foreign keys
  778.  *         ------------
  779.  * The CREATE TABLE may contain FOREIGN KEY clauses, so they get
  780.  * analyzed and ['foreign_keys'] is an array filled with
  781.  * the constraint name, the index list,
  782.  * the REFERENCES table name and REFERENCES index list,
  783.  * and ON UPDATE | ON DELETE clauses
  784.  *
  785.  * lem9: position_of_first_select
  786.  *       ------------------------
  787.  *
  788.  * The array index of the first SELECT we find. Will be used to
  789.  * insert a SQL_CALC_FOUND_ROWS.
  790.  *
  791.  * End of description of analyzer results
  792.  */
  793.  
  794.         // must be sorted
  795.         // TODO: current logic checks for only one word, so I put only the
  796.         // first word of the reserved expressions that end a table ref;
  797.         // maybe this is not ok (the first word might mean something else)
  798. //        $words_ending_table_ref = array(
  799. //            'FOR UPDATE',
  800. //            'GROUP BY',
  801. //            'HAVING',
  802. //            'LIMIT',
  803. //            'LOCK IN SHARE MODE',
  804. //            'ORDER BY',
  805. //            'PROCEDURE',
  806. //            'UNION',
  807. //            'WHERE'
  808. //        );
  809.         $words_ending_table_ref = array(
  810.             'FOR',
  811.             'GROUP',
  812.             'HAVING',
  813.             'LIMIT',
  814.             'LOCK',
  815.             'ORDER',
  816.             'PROCEDURE',
  817.             'UNION',
  818.             'WHERE'
  819.         );
  820.         $words_ending_table_ref_cnt = 9; //count($words_ending_table_ref);
  821.  
  822.         $words_ending_clauses = array(
  823.             'FOR',
  824.             'LIMIT',
  825.             'LOCK',
  826.             'PROCEDURE',
  827.             'UNION'
  828.         );
  829.         $words_ending_clauses_cnt = 5; //count($words_ending_clauses);
  830.  
  831.  
  832.  
  833.  
  834.         // must be sorted
  835.         $supported_query_types = array(
  836.             'SELECT'
  837.             /*
  838.             // Support for these additional query types will come later on.
  839.             'DELETE',
  840.             'INSERT',
  841.             'REPLACE',
  842.             'TRUNCATE',
  843.             'UPDATE'
  844.             'EXPLAIN',
  845.             'DESCRIBE',
  846.             'SHOW',
  847.             'CREATE',
  848.             'SET',
  849.             'ALTER'
  850.             */
  851.         );
  852.         $supported_query_types_cnt = count($supported_query_types);
  853.  
  854.         // loop #1 for each token: select_expr, table_ref for SELECT
  855.  
  856.         for ($i = 0; $i < $size; $i++) {
  857. //DEBUG echo "trace loop1 <b>"  . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br />";
  858.  
  859.             // High speed seek for locating the end of the current query
  860.             if ($seek_queryend == TRUE) {
  861.                 if ($arr[$i]['type'] == 'punct_queryend') {
  862.                     $seek_queryend = FALSE;
  863.                 } else {
  864.                     continue;
  865.                 } // end if (type == punct_queryend)
  866.             } // end if ($seek_queryend)
  867.  
  868.             // TODO: when we find a UNION, should we split
  869.             // in another subresult?
  870.             if ($arr[$i]['type'] == 'punct_queryend') {
  871.                 $result[]  = $subresult;
  872.                 $subresult = $subresult_empty;
  873.                 continue;
  874.             } // end if (type == punct_queryend)
  875.  
  876. // ==============================================================
  877.             if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  878.                 if ($in_extract) {
  879.                     $number_of_brackets_in_extract++;
  880.                 }
  881.                 if ($in_group_concat) {
  882.                     $number_of_brackets_in_group_concat++;
  883.                 }
  884.             }
  885. // ==============================================================
  886.             if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  887.                 if ($in_extract) {
  888.                     $number_of_brackets_in_extract--;
  889.                     if ($number_of_brackets_in_extract == 0) {
  890.                        $in_extract = FALSE;
  891.                     }
  892.                 }
  893.                 if ($in_group_concat) {
  894.                     $number_of_brackets_in_group_concat--;
  895.                     if ($number_of_brackets_in_group_concat == 0) {
  896.                        $in_group_concat = FALSE;
  897.                     }
  898.                 }
  899.             }
  900. // ==============================================================
  901.             if ($arr[$i]['type'] == 'alpha_functionName') {
  902.                 $upper_data = strtoupper($arr[$i]['data']);
  903.                 if ($upper_data =='EXTRACT') {
  904.                     $in_extract = TRUE;
  905.                     $number_of_brackets_in_extract = 0;
  906.                 }
  907.                 if ($upper_data =='GROUP_CONCAT') {
  908.                     $in_group_concat = TRUE;
  909.                     $number_of_brackets_in_group_concat = 0;
  910.                 }
  911.             }
  912.  
  913. // ==============================================================
  914.             if ($arr[$i]['type'] == 'alpha_reservedWord') {
  915.                 // We don't know what type of query yet, so run this
  916.                 if ($subresult['querytype'] == '') {
  917.                     $subresult['querytype'] = strtoupper($arr[$i]['data']);
  918.                 } // end if (querytype was empty)
  919.  
  920.                 // Check if we support this type of query
  921.                 if (!PMA_STR_binarySearchInArr($subresult['querytype'], $supported_query_types, $supported_query_types_cnt)) {
  922.                     // Skip ahead to the next one if we don't
  923.                     $seek_queryend = TRUE;
  924.                     continue;
  925.                 } // end if (query not supported)
  926.  
  927.                 // upper once
  928.                 $upper_data = strtoupper($arr[$i]['data']);
  929.                 //TODO: reset for each query?
  930.  
  931.                 if ($upper_data == 'SELECT') {
  932.                     $seen_from = FALSE;
  933.                     $previous_was_identifier = FALSE;
  934.                     $current_select_expr = -1;
  935.                     $seen_end_of_table_ref = FALSE;
  936.                 } // end if ( data == SELECT)
  937.  
  938.                 if ($upper_data =='FROM' && !$in_extract) {
  939.                     $current_table_ref = -1;
  940.                     $seen_from = TRUE;
  941.                     $previous_was_identifier = FALSE;
  942.                     $save_table_ref = TRUE;
  943.                 } // end if (data == FROM)
  944.  
  945.                 // here, do not 'continue' the loop, as we have more work for
  946.                 // reserved words below
  947.             } // end if (type == alpha_reservedWord)
  948.  
  949. // ==============================
  950.             if (($arr[$i]['type'] == 'quote_backtick')
  951.              || ($arr[$i]['type'] == 'quote_double')
  952.              || ($arr[$i]['type'] == 'quote_single')
  953.              || ($arr[$i]['type'] == 'alpha_identifier')) {
  954.  
  955.                 switch ($arr[$i]['type']) {
  956.                     case 'alpha_identifier':
  957.                         $identifier = $arr[$i]['data'];
  958.                         break;
  959.  
  960.                 //TODO: check embedded double quotes or backticks?
  961.                 // and/or remove just the first and last character?
  962.                     case 'quote_backtick':
  963.                         $identifier = str_replace('`','',$arr[$i]['data']);
  964.                         break;
  965.                     case 'quote_double':
  966.                         $identifier = str_replace('"','',$arr[$i]['data']);
  967.                         break;
  968.                     case 'quote_single':
  969.                         $identifier = str_replace("'","",$arr[$i]['data']);
  970.                         break;
  971.                 } // end switch
  972.  
  973.                 if ($subresult['querytype'] == 'SELECT' && !$in_group_concat) {
  974.                     if (!$seen_from) {
  975.                         if ($previous_was_identifier && isset($chain)) {
  976.                             // found alias for this select_expr, save it
  977.                             // but only if we got something in $chain
  978.                             // (for example, SELECT COUNT(*) AS cnt
  979.                             // puts nothing in $chain, so we avoid
  980.                             // setting the alias)
  981.                             $alias_for_select_expr = $identifier;
  982.                         } else {
  983.                             $chain[] = $identifier;
  984.                             $previous_was_identifier = TRUE;
  985.  
  986.                         } // end if !$previous_was_identifier
  987.                     } else {
  988.                         // ($seen_from)
  989.                         if ($save_table_ref && !$seen_end_of_table_ref) {
  990.                             if ($previous_was_identifier) {
  991.                                 // found alias for table ref
  992.                                 // save it for later
  993.                                 $alias_for_table_ref = $identifier;
  994.                             } else {
  995.                                 $chain[] = $identifier;
  996.                                 $previous_was_identifier = TRUE;
  997.  
  998.                             } // end if ($previous_was_identifier)
  999.                         } // end if ($save_table_ref &&!$seen_end_of_table_ref)
  1000.                     } // end if (!$seen_from)
  1001.                 } // end if (querytype SELECT)
  1002.             } // end if ( quote_backtick or double quote or alpha_identifier)
  1003.  
  1004. // ===================================
  1005.             if ($arr[$i]['type'] == 'punct_qualifier') {
  1006.                 // to be able to detect an identifier following another
  1007.                 $previous_was_identifier = FALSE;
  1008.                 continue;
  1009.             } // end if (punct_qualifier)
  1010.  
  1011.             // TODO: check if 3 identifiers following one another -> error
  1012.  
  1013.             //    s a v e    a    s e l e c t    e x p r
  1014.             // finding a list separator or FROM
  1015.             // means that we must save the current chain of identifiers
  1016.             // into a select expression
  1017.  
  1018.             // for now, we only save a select expression if it contains
  1019.             // at least one identifier, as we are interested in checking
  1020.             // the columns and table names, so in "select * from persons",
  1021.             // the "*" is not saved
  1022.  
  1023.             if (isset($chain) && !$seen_end_of_table_ref
  1024.                && (   (!$seen_from
  1025.                    && $arr[$i]['type'] == 'punct_listsep')
  1026.                   || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data == 'FROM')) ) {
  1027.                 $size_chain = count($chain);
  1028.                 $current_select_expr++;
  1029.                 $subresult['select_expr'][$current_select_expr] = array(
  1030.                   'expr' => '',
  1031.                   'alias' => '',
  1032.                   'db'   => '',
  1033.                   'table_name' => '',
  1034.                   'table_true_name' => '',
  1035.                   'column' => ''
  1036.                  );
  1037.  
  1038.                 if (!empty($alias_for_select_expr)) {
  1039.                     // we had found an alias for this select expression
  1040.                     $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr;
  1041.                     unset($alias_for_select_expr);
  1042.                 }
  1043.                 // there is at least a column
  1044.                 $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1];
  1045.                 $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1];
  1046.  
  1047.                 // maybe a table
  1048.                 if ($size_chain > 1) {
  1049.                     $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2];
  1050.                     // we assume for now that this is also the true name
  1051.                     $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2];
  1052.                     $subresult['select_expr'][$current_select_expr]['expr']
  1053.                      = $subresult['select_expr'][$current_select_expr]['table_name']
  1054.                       . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
  1055.                 } // end if ($size_chain > 1)
  1056.  
  1057.                 // maybe a db
  1058.                 if ($size_chain > 2) {
  1059.                     $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3];
  1060.                     $subresult['select_expr'][$current_select_expr]['expr']
  1061.                      = $subresult['select_expr'][$current_select_expr]['db']
  1062.                       . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
  1063.                 } // end if ($size_chain > 2)
  1064.                 unset($chain);
  1065.  
  1066.                 // TODO: explain this:
  1067.                 if (($arr[$i]['type'] == 'alpha_reservedWord')
  1068.                  && ($upper_data != 'FROM')) {
  1069.                     $previous_was_identifier = TRUE;
  1070.                 }
  1071.  
  1072.             } // end if (save a select expr)
  1073.  
  1074.  
  1075.             //======================================
  1076.             //    s a v e    a    t a b l e    r e f
  1077.             //======================================
  1078.  
  1079.             // maybe we just saw the end of table refs
  1080.             // but the last table ref has to be saved
  1081.             // or we are at the last token (TODO: there could be another
  1082.             // query after this one)
  1083.             // or we just got a reserved word
  1084.  
  1085.             if (isset($chain) && $seen_from && $save_table_ref
  1086.              && ($arr[$i]['type'] == 'punct_listsep'
  1087.                || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS")
  1088.                || $seen_end_of_table_ref
  1089.                || $i==$size-1 )) {
  1090.  
  1091.                 $size_chain = count($chain);
  1092.                 $current_table_ref++;
  1093.                 $subresult['table_ref'][$current_table_ref] = array(
  1094.                   'expr'            => '',
  1095.                   'db'              => '',
  1096.                   'table_name'      => '',
  1097.                   'table_alias'     => '',
  1098.                   'table_true_name' => ''
  1099.                  );
  1100.                 if (!empty($alias_for_table_ref)) {
  1101.                     $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref;
  1102.                     unset($alias_for_table_ref);
  1103.                 }
  1104.                 $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1];
  1105.                 // we assume for now that this is also the true name
  1106.                 $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1];
  1107.                 $subresult['table_ref'][$current_table_ref]['expr']
  1108.                      = $subresult['table_ref'][$current_table_ref]['table_name'];
  1109.                 // maybe a db
  1110.                 if ($size_chain > 1) {
  1111.                     $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2];
  1112.                     $subresult['table_ref'][$current_table_ref]['expr']
  1113.                      = $subresult['table_ref'][$current_table_ref]['db']
  1114.                       . '.' . $subresult['table_ref'][$current_table_ref]['expr'];
  1115.                 } // end if ($size_chain > 1)
  1116.  
  1117.                 // add the table alias into the whole expression
  1118.                 $subresult['table_ref'][$current_table_ref]['expr']
  1119.                  .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias'];
  1120.  
  1121.                 unset($chain);
  1122.                 $previous_was_identifier = TRUE;
  1123.                 //continue;
  1124.  
  1125.             } // end if (save a table ref)
  1126.  
  1127.  
  1128.             // when we have found all table refs,
  1129.             // for each table_ref alias, put the true name of the table
  1130.             // in the corresponding select expressions
  1131.  
  1132.             if (isset($current_table_ref) && ($seen_end_of_table_ref || $i == $size-1)) {
  1133.                 for ($tr=0; $tr <= $current_table_ref; $tr++) {
  1134.                     $alias = $subresult['table_ref'][$tr]['table_alias'];
  1135.                     $truename = $subresult['table_ref'][$tr]['table_true_name'];
  1136.                     for ($se=0; $se <= $current_select_expr; $se++) {
  1137.                         if (!empty($alias) && $subresult['select_expr'][$se]['table_true_name']
  1138.                            == $alias) {
  1139.                             $subresult['select_expr'][$se]['table_true_name']
  1140.                              = $truename;
  1141.                         } // end if (found the alias)
  1142.                     } // end for (select expressions)
  1143.  
  1144.                 } // end for (table refs)
  1145.             } // end if (set the true names)
  1146.  
  1147.  
  1148.            // e n d i n g    l o o p  #1
  1149.            // set the $previous_was_identifier to FALSE if the current
  1150.            // token is not an identifier
  1151.            if (($arr[$i]['type'] != 'alpha_identifier')
  1152.             && ($arr[$i]['type'] != 'quote_double')
  1153.             && ($arr[$i]['type'] != 'quote_single')
  1154.             && ($arr[$i]['type'] != 'quote_backtick')) {
  1155.                $previous_was_identifier = FALSE;
  1156.            } // end if
  1157.  
  1158.            // however, if we are on AS, we must keep the $previous_was_identifier
  1159.            if (($arr[$i]['type'] == 'alpha_reservedWord')
  1160.             && ($upper_data == 'AS'))  {
  1161.                $previous_was_identifier = TRUE;
  1162.            }
  1163.  
  1164.            if (($arr[$i]['type'] == 'alpha_reservedWord')
  1165.             && ($upper_data =='ON' || $upper_data =='USING')) {
  1166.                $save_table_ref = FALSE;
  1167.            } // end if (data == ON)
  1168.  
  1169.            if (($arr[$i]['type'] == 'alpha_reservedWord')
  1170.             && ($upper_data =='JOIN' || $upper_data =='FROM')) {
  1171.                $save_table_ref = TRUE;
  1172.            } // end if (data == JOIN)
  1173.  
  1174.            // no need to check the end of table ref if we already did
  1175.            // TODO: maybe add "&& $seen_from"
  1176.            if (!$seen_end_of_table_ref) {
  1177.                // if this is the last token, it implies that we have
  1178.                // seen the end of table references
  1179.                // Check for the end of table references
  1180.                //
  1181.                // Note: if we are analyzing a GROUP_CONCAT clause,
  1182.                // we might find a word that seems to indicate that
  1183.                // we have found the end of table refs (like ORDER)
  1184.                // but it's a modifier of the GROUP_CONCAT so
  1185.                // it's not the real end of table refs
  1186.                if (($i == $size-1)
  1187.                || ($arr[$i]['type'] == 'alpha_reservedWord'
  1188.                   && !$in_group_concat
  1189.                   && PMA_STR_binarySearchInArr($upper_data, $words_ending_table_ref, $words_ending_table_ref_cnt))) {
  1190.                    $seen_end_of_table_ref = TRUE;
  1191.                    // to be able to save the last table ref, but do not
  1192.                    // set it true if we found a word like "ON" that has
  1193.                    // already set it to false
  1194.                    if (isset($save_table_ref) && $save_table_ref != FALSE) {
  1195.                       $save_table_ref = TRUE;
  1196.                    } //end if
  1197.  
  1198.                } // end if (check for end of table ref)
  1199.            } //end if (!$seen_end_of_table_ref)
  1200.  
  1201.            if ($seen_end_of_table_ref) {
  1202.                $save_table_ref = FALSE;
  1203.            } // end if
  1204.  
  1205.         } // end for $i (loop #1)
  1206.  
  1207.         // -------------------------------------------------------
  1208.         // This is a big hunk of debugging code by Marc for this.
  1209.         // -------------------------------------------------------
  1210.         /*
  1211.           if (isset($current_select_expr)) {
  1212.            for ($trace=0; $trace<=$current_select_expr; $trace++) {
  1213.                echo "<br />";
  1214.                reset ($subresult['select_expr'][$trace]);
  1215.                while (list ($key, $val) = each ($subresult['select_expr'][$trace]))
  1216.                    echo "sel expr $trace $key => $val<br />\n";
  1217.                }
  1218.           }
  1219.  
  1220.           if (isset($current_table_ref)) {
  1221.            echo "current_table_ref = " . $current_table_ref . "<br>";
  1222.            for ($trace=0; $trace<=$current_table_ref; $trace++) {
  1223.  
  1224.                echo "<br />";
  1225.                reset ($subresult['table_ref'][$trace]);
  1226.                while (list ($key, $val) = each ($subresult['table_ref'][$trace]))
  1227.                echo "table ref $trace $key => $val<br />\n";
  1228.                }
  1229.           }
  1230.         */
  1231.         // -------------------------------------------------------
  1232.  
  1233.  
  1234.         // loop #2: for queryflags
  1235.         //          ,querytype (for queries != 'SELECT')
  1236.         //
  1237.         // we will also need this queryflag in loop 2
  1238.         // so set it here
  1239.         if (isset($current_table_ref) && $current_table_ref > -1) {
  1240.             $subresult['queryflags']['select_from'] = 1;
  1241.         }
  1242.  
  1243.         $seen_reserved_word = FALSE;
  1244.         $seen_group = FALSE;
  1245.         $seen_order = FALSE;
  1246.         $in_group_by = FALSE; // true when we are inside the GROUP BY clause
  1247.         $in_order_by = FALSE; // true when we are inside the ORDER BY clause
  1248.         $in_having = FALSE; // true when we are inside the HAVING clause
  1249.         $in_select_expr = FALSE; // true when we are inside the select expr clause
  1250.         $in_where = FALSE; // true when we are inside the WHERE clause
  1251.         $in_from = FALSE;
  1252.         $in_group_concat = FALSE;
  1253.         $unsorted_query = '';
  1254.  
  1255.         for ($i = 0; $i < $size; $i++) {
  1256. //DEBUG echo "trace loop2 <b>"  . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br />";
  1257.  
  1258.            // need_confirm
  1259.            //
  1260.            // check for reserved words that will have to generate
  1261.            // a confirmation request later in sql.php
  1262.            // the cases are:
  1263.            //   DROP TABLE
  1264.            //   DROP DATABASE
  1265.            //   ALTER TABLE... DROP
  1266.            //   DELETE FROM...
  1267.            //
  1268.            // this code is not used for confirmations coming from functions.js
  1269.  
  1270.            // TODO: check for punct_queryend
  1271.  
  1272.            if ($arr[$i]['type'] == 'alpha_reservedWord') {
  1273.                $upper_data = strtoupper($arr[$i]['data']);
  1274.                if (!$seen_reserved_word) {
  1275.                    $first_reserved_word = $upper_data;
  1276.                    $subresult['querytype'] = $upper_data;
  1277.                    $seen_reserved_word = TRUE;
  1278.  
  1279.                    // if the first reserved word is DROP or DELETE,
  1280.                    // we know this is a query that needs to be confirmed
  1281.                    if ($first_reserved_word=='DROP'
  1282.                        || $first_reserved_word == 'DELETE'
  1283.                        || $first_reserved_word == 'TRUNCATE') {
  1284.                       $subresult['queryflags']['need_confirm'] = 1;
  1285.                    }
  1286.  
  1287.                    if ($first_reserved_word=='SELECT'){
  1288.                        $position_of_first_select = $i;
  1289.                    }
  1290.  
  1291.                } else {
  1292.                    if ($upper_data=='DROP' && $first_reserved_word=='ALTER') {
  1293.                       $subresult['queryflags']['need_confirm'] = 1;
  1294.                    }
  1295.                }
  1296.  
  1297.                if ($upper_data == 'SELECT') {
  1298.                    $in_select_expr = TRUE;
  1299.                    $select_expr_clause = '';
  1300.                }
  1301.                if ($upper_data == 'DISTINCT' && !$in_group_concat) {
  1302.                       $subresult['queryflags']['distinct'] = 1;
  1303.                }
  1304.  
  1305.                if ($upper_data == 'UNION') {
  1306.                       $subresult['queryflags']['union'] = 1;
  1307.                }
  1308.  
  1309.                if ($upper_data == 'JOIN') {
  1310.                       $subresult['queryflags']['join'] = 1;
  1311.                }
  1312.  
  1313.                if ($upper_data == 'OFFSET') {
  1314.                       $subresult['queryflags']['offset'] = 1;
  1315.                }
  1316.  
  1317.                // if this is a real SELECT...FROM
  1318.                if ($upper_data == 'FROM' && isset($subresult['queryflags']['select_from']) && $subresult['queryflags']['select_from'] == 1) {
  1319.                    $in_from = TRUE;
  1320.                    $from_clause = '';
  1321.                    $in_select_expr = FALSE;
  1322.                }
  1323.  
  1324.  
  1325.                // (we could have less resetting of variables to FALSE
  1326.                // if we trust that the query respects the standard
  1327.                // MySQL order for clauses)
  1328.  
  1329.                // we use $seen_group and $seen_order because we are looking
  1330.                // for the BY
  1331.                if ($upper_data == 'GROUP') {
  1332.                    $seen_group = TRUE;
  1333.                    $seen_order = FALSE;
  1334.                    $in_having = FALSE;
  1335.                    $in_order_by = FALSE;
  1336.                    $in_where = FALSE;
  1337.                    $in_select_expr = FALSE;
  1338.                    $in_from = FALSE;
  1339.                }
  1340.                if ($upper_data == 'ORDER' && !$in_group_concat) {
  1341.                    $seen_order = TRUE;
  1342.                    $seen_group = FALSE;
  1343.                    $in_having = FALSE;
  1344.                    $in_group_by = FALSE;
  1345.                    $in_where = FALSE;
  1346.                    $in_select_expr = FALSE;
  1347.                    $in_from = FALSE;
  1348.                }
  1349.                if ($upper_data == 'HAVING') {
  1350.                    $in_having = TRUE;
  1351.                    $having_clause = '';
  1352.                    $seen_group = FALSE;
  1353.                    $seen_order = FALSE;
  1354.                    $in_group_by = FALSE;
  1355.                    $in_order_by = FALSE;
  1356.                    $in_where = FALSE;
  1357.                    $in_select_expr = FALSE;
  1358.                    $in_from = FALSE;
  1359.                }
  1360.  
  1361.                if ($upper_data == 'WHERE') {
  1362.                    $in_where = TRUE;
  1363.                    $where_clause = '';
  1364.                    $where_clause_identifiers = array();
  1365.                    $seen_group = FALSE;
  1366.                    $seen_order = FALSE;
  1367.                    $in_group_by = FALSE;
  1368.                    $in_order_by = FALSE;
  1369.                    $in_having = FALSE;
  1370.                    $in_select_expr = FALSE;
  1371.                    $in_from = FALSE;
  1372.                }
  1373.  
  1374.                if ($upper_data == 'BY') {
  1375.                    if ($seen_group) {
  1376.                        $in_group_by = TRUE;
  1377.                        $group_by_clause = '';
  1378.                    }
  1379.                    if ($seen_order) {
  1380.                        $in_order_by = TRUE;
  1381.                        $order_by_clause = '';
  1382.                    }
  1383.                }
  1384.  
  1385.                // if we find one of the words that could end the clause
  1386.                if (PMA_STR_binarySearchInArr($upper_data, $words_ending_clauses, $words_ending_clauses_cnt)) {
  1387.  
  1388.                    $in_group_by = FALSE;
  1389.                    $in_order_by = FALSE;
  1390.                    $in_having   = FALSE;
  1391.                    $in_where    = FALSE;
  1392.                    $in_select_expr = FALSE;
  1393.                    $in_from = FALSE;
  1394.                }
  1395.  
  1396.            } // endif (reservedWord)
  1397.  
  1398.  
  1399.            // do not add a blank after a function name
  1400.            // TODO: can we combine loop 2 and loop 1?
  1401.            // some code is repeated here...
  1402.  
  1403.            $sep=' ';
  1404.            if ($arr[$i]['type'] == 'alpha_functionName') {
  1405.                $sep='';
  1406.                $upper_data = strtoupper($arr[$i]['data']);
  1407.                if ($upper_data =='GROUP_CONCAT') {
  1408.                    $in_group_concat = TRUE;
  1409.                    $number_of_brackets_in_group_concat = 0;
  1410.                }
  1411.            }
  1412.  
  1413.            if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  1414.                if ($in_group_concat) {
  1415.                   $number_of_brackets_in_group_concat++;
  1416.                }
  1417.            }
  1418.            if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  1419.                if ($in_group_concat) {
  1420.                   $number_of_brackets_in_group_concat--;
  1421.                   if ($number_of_brackets_in_group_concat == 0) {
  1422.                      $in_group_concat = FALSE;
  1423.                   }
  1424.                }
  1425.            }
  1426.  
  1427.            if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') {
  1428.                $select_expr_clause .= $arr[$i]['data'] . $sep;
  1429.            }
  1430.            if ($in_from && $upper_data != 'FROM') {
  1431.                $from_clause .= $arr[$i]['data'] . $sep;
  1432.            }
  1433.            if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') {
  1434.                $group_by_clause .= $arr[$i]['data'] . $sep;
  1435.            }
  1436.            if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') {
  1437.                $order_by_clause .= $arr[$i]['data'] . $sep;
  1438.            }
  1439.            if ($in_having && $upper_data != 'HAVING') {
  1440.                $having_clause .= $arr[$i]['data'] . $sep;
  1441.            }
  1442.            if ($in_where && $upper_data != 'WHERE') {
  1443.                $where_clause .= $arr[$i]['data'] . $sep;
  1444.  
  1445.                if (($arr[$i]['type'] == 'quote_backtick')
  1446.                 || ($arr[$i]['type'] == 'alpha_identifier')) {
  1447.                    $where_clause_identifiers[] = $arr[$i]['data'];
  1448.                }
  1449.            }
  1450.  
  1451.            // FIXME: is it correct to always add $sep ?
  1452.            if (isset($subresult['queryflags']['select_from'])
  1453.              && $subresult['queryflags']['select_from'] == 1
  1454.              && !$seen_order) {
  1455.                $unsorted_query .= $arr[$i]['data'] . $sep;
  1456.            }
  1457.  
  1458.            // clear $upper_data for next iteration
  1459.            $upper_data='';
  1460.  
  1461.         } // end for $i (loop #2)
  1462.  
  1463.         // -----------------------------------------------------
  1464.         // loop #3: foreign keys
  1465.         // (for now, check only the first query)
  1466.         // (for now, identifiers must be backquoted)
  1467.  
  1468.         $seen_foreign = FALSE;
  1469.         $seen_references = FALSE;
  1470.         $seen_constraint = FALSE;
  1471.         $in_bracket = FALSE;
  1472.         $foreign_key_number = -1;
  1473.  
  1474.         for ($i = 0; $i < $size; $i++) {
  1475.         // DEBUG echo "<b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br />";
  1476.             if ($arr[$i]['type'] == 'alpha_reservedWord') {
  1477.                $upper_data = strtoupper($arr[$i]['data']);
  1478.  
  1479.                if ($upper_data == 'CONSTRAINT') {
  1480.                    $foreign_key_number++;
  1481.                    $seen_foreign = FALSE;
  1482.                    $seen_references = FALSE;
  1483.                    $seen_constraint = TRUE;
  1484.                }
  1485.                if ($upper_data == 'FOREIGN') {
  1486.                    $seen_foreign = TRUE;
  1487.                    $seen_references = FALSE;
  1488.                    $seen_constraint = FALSE;
  1489.                }
  1490.                if ($upper_data == 'REFERENCES') {
  1491.                    $seen_foreign = FALSE;
  1492.                    $seen_references = TRUE;
  1493.                    $seen_constraint = FALSE;
  1494.                }
  1495.  
  1496.  
  1497.               // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
  1498.               // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
  1499.  
  1500.               // but we set ['on_delete'] or ['on_cascade'] to
  1501.               // CASCADE | SET_NULL | NO_ACTION | RESTRICT
  1502.  
  1503.                if ($upper_data == 'ON') {
  1504.                    unset($clause);
  1505.                    if ($arr[$i+1]['type'] == 'alpha_reservedWord') {
  1506.                        $second_upper_data = strtoupper($arr[$i+1]['data']);
  1507.                        if ($second_upper_data == 'DELETE') {
  1508.                             $clause = 'on_delete';
  1509.                        }
  1510.                        if ($second_upper_data == 'UPDATE') {
  1511.                             $clause = 'on_update';
  1512.                        }
  1513.                        if (isset($clause)
  1514.                        && ($arr[$i+2]['type'] == 'alpha_reservedWord'
  1515.  
  1516.              // ugly workaround because currently, NO is not
  1517.              // in the list of reserved words in sqlparser.data
  1518.              // (we got a bug report about not being able to use
  1519.              // 'no' as an identifier)
  1520.                            || ($arr[$i+2]['type'] == 'alpha_identifier'
  1521.                               && strtoupper($arr[$i+2]['data'])=='NO') )
  1522.                           ) {
  1523.                           $third_upper_data = strtoupper($arr[$i+2]['data']);
  1524.                           if ($third_upper_data == 'CASCADE'
  1525.                            || $third_upper_data == 'RESTRICT') {
  1526.                               $value = $third_upper_data;
  1527.                           } elseif ($third_upper_data == 'SET'
  1528.                                  || $third_upper_data == 'NO') {
  1529.                               if ($arr[$i+3]['type'] == 'alpha_reservedWord') {
  1530.                                   $value = $third_upper_data . '_' . strtoupper($arr[$i+3]['data']);
  1531.                               }
  1532.                           } else {
  1533.                           // for example: ON UPDATE CURRENT_TIMESTAMP
  1534.                           // which is not for a foreign key
  1535.                               $value = '';
  1536.                           }
  1537.                           if (!empty($value)) {
  1538.                               $foreign[$foreign_key_number][$clause] = $value;
  1539.                           }
  1540.                        }
  1541.                    }
  1542.                }
  1543.  
  1544.             }
  1545.  
  1546.             if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  1547.                 $in_bracket = TRUE;
  1548.             }
  1549.  
  1550.             if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  1551.                 $in_bracket = FALSE;
  1552.                 if ($seen_references) {
  1553.                     $seen_references = FALSE;
  1554.                 }
  1555.             }
  1556.  
  1557.             if (($arr[$i]['type'] == 'quote_backtick')) {
  1558.  
  1559.                 if ($seen_constraint) {
  1560.                     // remove backquotes
  1561.                     $identifier = str_replace('`','',$arr[$i]['data']);
  1562.                     $foreign[$foreign_key_number]['constraint'] = $identifier;
  1563.                 }
  1564.                 if ($seen_foreign && $in_bracket) {
  1565.                     // remove backquotes
  1566.                     $identifier = str_replace('`','',$arr[$i]['data']);
  1567.                     $foreign[$foreign_key_number]['index_list'][] = $identifier;
  1568.                 }
  1569.  
  1570.                 if ($seen_references) {
  1571.                     $identifier = str_replace('`','',$arr[$i]['data']);
  1572.                     if ($in_bracket) {
  1573.                         $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
  1574.                     } else {
  1575.                         // for MySQL 4.0.18, identifier is
  1576.                         // `table` or `db`.`table`
  1577.                         // first pass will pick the db name
  1578.                         // next pass will execute the else and pick the
  1579.                         // db name in $db_table[0]
  1580.                         if ($arr[$i+1]['type'] == 'punct_qualifier') {
  1581.                                 $foreign[$foreign_key_number]['ref_db_name'] = $identifier;
  1582.                         } else {
  1583.                         // for MySQL 4.0.16, identifier is
  1584.                         // `table` or `db.table`
  1585.                             $db_table = explode('.',$identifier);
  1586.                             if (isset($db_table[1])) {
  1587.                                 $foreign[$foreign_key_number]['ref_db_name'] = $db_table[0];
  1588.                                 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[1];
  1589.                             } else {
  1590.                                 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[0];
  1591.                             }
  1592.                         }
  1593.                     }
  1594.                 }
  1595.             }
  1596.         } // end for $i (loop #3)
  1597.  
  1598.         if (isset($foreign)) {
  1599.             $subresult['foreign_keys'] = $foreign;
  1600.         }
  1601.         //DEBUG print_r($foreign);
  1602.  
  1603.         if (isset($select_expr_clause)) {
  1604.             $subresult['select_expr_clause'] = $select_expr_clause;
  1605.         }
  1606.         if (isset($from_clause)) {
  1607.             $subresult['from_clause'] = $from_clause;
  1608.         }
  1609.         if (isset($group_by_clause)) {
  1610.             $subresult['group_by_clause'] = $group_by_clause;
  1611.         }
  1612.         if (isset($order_by_clause)) {
  1613.             $subresult['order_by_clause'] = $order_by_clause;
  1614.         }
  1615.         if (isset($having_clause)) {
  1616.             $subresult['having_clause'] = $having_clause;
  1617.         }
  1618.         if (isset($where_clause)) {
  1619.             $subresult['where_clause'] = $where_clause;
  1620.         }
  1621.         if (isset($unsorted_query) && !empty($unsorted_query)) {
  1622.             $subresult['unsorted_query'] = $unsorted_query;
  1623.         }
  1624.         if (isset($where_clause_identifiers)) {
  1625.             $subresult['where_clause_identifiers'] = $where_clause_identifiers;
  1626.         }
  1627.  
  1628.         if (isset($position_of_first_select)) {
  1629.             $subresult['position_of_first_select'] = $position_of_first_select;
  1630.         }
  1631.  
  1632.         // They are naughty and didn't have a trailing semi-colon,
  1633.         // then still handle it properly
  1634.         if ($subresult['querytype'] != '') {
  1635.             $result[] = $subresult;
  1636.         }
  1637.         return $result;
  1638.     } // end of the "PMA_SQP_analyze()" function
  1639.  
  1640.  
  1641.     /**
  1642.      * Colorizes SQL queries html formatted
  1643.      *
  1644.      * @param  array   The SQL queries html formatted
  1645.      *
  1646.      * @return array   The colorized SQL queries
  1647.      *
  1648.      * @access public
  1649.      */
  1650.     function PMA_SQP_formatHtml_colorize($arr)
  1651.     {
  1652.         $i         = $GLOBALS['PMA_strpos']($arr['type'], '_');
  1653.         $class     = '';
  1654.         if ($i > 0) {
  1655.             $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' ';
  1656.         }
  1657.  
  1658.         $class     .= 'syntax_' . $arr['type'];
  1659.  
  1660.         //TODO: check why adding a "\n" after the </span> would cause extra
  1661.         //      blanks to be displayed:
  1662.         //      SELECT p . person_name
  1663.  
  1664.         return '<span class="' . $class . '">' . htmlspecialchars($arr['data']) . '</span>';
  1665.     } // end of the "PMA_SQP_formatHtml_colorize()" function
  1666.  
  1667.  
  1668.     /**
  1669.      * Formats SQL queries to html
  1670.      *
  1671.      * @param  array   The SQL queries
  1672.      * @param  string  mode
  1673.      * @param  integer starting token
  1674.      * @param  integer number of tokens to format, -1 = all
  1675.      *
  1676.      * @return string  The formatted SQL queries
  1677.      *
  1678.      * @access public
  1679.      */
  1680.     function PMA_SQP_formatHtml($arr, $mode='color', $start_token=0,
  1681.         $number_of_tokens=-1)
  1682.     {
  1683.         // then check for an array
  1684.         if (!is_array($arr)) {
  1685.             return htmlspecialchars($arr);
  1686.         }
  1687.         // first check for the SQL parser having hit an error
  1688.         if (PMA_SQP_isError()) {
  1689.             return htmlspecialchars($arr['raw']);
  1690.         }
  1691.         // else do it properly
  1692.         switch ($mode) {
  1693.             case 'color':
  1694.                 $str                                = '<span class="syntax">';
  1695.                 $html_line_break                    = '<br />';
  1696.                 break;
  1697.             case 'query_only':
  1698.                 $str                                = '';
  1699.                 $html_line_break                    = "\n";
  1700.                 break;
  1701.             case 'text':
  1702.                 $str                                = '';
  1703.                 $html_line_break                    = '<br />';
  1704.                 break;
  1705.         } // end switch
  1706.         $indent                                     = 0;
  1707.         $bracketlevel                               = 0;
  1708.         $functionlevel                              = 0;
  1709.         $infunction                                 = FALSE;
  1710.         $space_punct_listsep                        = ' ';
  1711.         $space_punct_listsep_function_name          = ' ';
  1712.         // $space_alpha_reserved_word = '<br />'."\n";
  1713.         $space_alpha_reserved_word                  = ' ';
  1714.  
  1715.         $keywords_with_brackets_1before            = array(
  1716.             'INDEX',
  1717.             'KEY',
  1718.             'ON',
  1719.             'USING'
  1720.         );
  1721.         $keywords_with_brackets_1before_cnt        = 4;
  1722.  
  1723.         $keywords_with_brackets_2before            = array(
  1724.             'IGNORE',
  1725.             'INDEX',
  1726.             'INTO',
  1727.             'KEY',
  1728.             'PRIMARY',
  1729.             'PROCEDURE',
  1730.             'REFERENCES',
  1731.             'UNIQUE',
  1732.             'USE'
  1733.         );
  1734.         // $keywords_with_brackets_2before_cnt = count($keywords_with_brackets_2before);
  1735.         $keywords_with_brackets_2before_cnt        = 9;
  1736.  
  1737.         // These reserved words do NOT get a newline placed near them.
  1738.         $keywords_no_newline               = array(
  1739.             'AS',
  1740.             'ASC',
  1741.             'DESC',
  1742.             'DISTINCT',
  1743.             'HOUR',
  1744.             'INTERVAL',
  1745.             'IS',
  1746.             'LIKE',
  1747.             'NOT',
  1748.             'NULL',
  1749.             'ON',
  1750.             'REGEXP'
  1751.         );
  1752.         $keywords_no_newline_cnt           = 12;
  1753.  
  1754.         // These reserved words introduce a privilege list
  1755.         $keywords_priv_list                = array(
  1756.             'GRANT',
  1757.             'REVOKE'
  1758.         );
  1759.         $keywords_priv_list_cnt            = 2;
  1760.  
  1761.         if ($number_of_tokens == -1) {
  1762.             $arraysize = $arr['len'];
  1763.         } else {
  1764.             $arraysize = $number_of_tokens;
  1765.         }
  1766.         $typearr   = array();
  1767.         if ($arraysize >= 0) {
  1768.             $typearr[0] = '';
  1769.             $typearr[1] = '';
  1770.             $typearr[2] = '';
  1771.             //$typearr[3] = $arr[0]['type'];
  1772.             $typearr[3] = $arr[$start_token]['type'];
  1773.         }
  1774.  
  1775.         $in_priv_list = FALSE;
  1776.         for ($i = $start_token; $i < $arraysize; $i++) {
  1777. // DEBUG echo "<b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br />";
  1778.             $before = '';
  1779.             $after  = '';
  1780.             $indent = 0;
  1781.             // array_shift($typearr);
  1782.             /*
  1783.             0 prev2
  1784.             1 prev
  1785.             2 current
  1786.             3 next
  1787.             */
  1788.             if (($i + 1) < $arraysize) {
  1789.                 // array_push($typearr, $arr[$i + 1]['type']);
  1790.                 $typearr[4] = $arr[$i + 1]['type'];
  1791.             } else {
  1792.                 //array_push($typearr, NULL);
  1793.                 $typearr[4] = '';
  1794.             }
  1795.  
  1796.             for ($j=0; $j<4; $j++) {
  1797.                 $typearr[$j] = $typearr[$j + 1];
  1798.             }
  1799.  
  1800.             switch ($typearr[2]) {
  1801.                 case 'white_newline':
  1802.                     $before     = '';
  1803.                     break;
  1804.                 case 'punct_bracket_open_round':
  1805.                     $bracketlevel++;
  1806.                     $infunction = FALSE;
  1807.                     // Make sure this array is sorted!
  1808.                     if (($typearr[1] == 'alpha_functionName') || ($typearr[1] == 'alpha_columnType') || ($typearr[1] == 'punct')
  1809.                         || ($typearr[3] == 'digit_integer') || ($typearr[3] == 'digit_hex') || ($typearr[3] == 'digit_float')
  1810.                         || (($typearr[0] == 'alpha_reservedWord')
  1811.                             && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 2]['data']), $keywords_with_brackets_2before, $keywords_with_brackets_2before_cnt))
  1812.                         || (($typearr[1] == 'alpha_reservedWord')
  1813.                             && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_with_brackets_1before, $keywords_with_brackets_1before_cnt))
  1814.                         ) {
  1815.                         $functionlevel++;
  1816.                         $infunction = TRUE;
  1817.                         $after      .= ' ';
  1818.                     } else {
  1819.                         $indent++;
  1820.                         $after      .= ($mode != 'query_only' ? '<div class="syntax_indent' . $indent . '">' : ' ');
  1821.                     }
  1822.                     break;
  1823.                 case 'alpha_identifier':
  1824.                     if (($typearr[1] == 'punct_qualifier') || ($typearr[3] == 'punct_qualifier')) {
  1825.                         $after      = '';
  1826.                         $before     = '';
  1827.                     }
  1828.                     if (($typearr[3] == 'alpha_columnType') || ($typearr[3] == 'alpha_identifier')) {
  1829.                         $after      .= ' ';
  1830.                     }
  1831.                     break;
  1832.                 case 'punct_qualifier':
  1833.                     $before         = '';
  1834.                     $after          = '';
  1835.                     break;
  1836.                 case 'punct_listsep':
  1837.                     if ($infunction == TRUE) {
  1838.                         $after      .= $space_punct_listsep_function_name;
  1839.                     } else {
  1840.                         $after      .= $space_punct_listsep;
  1841.                     }
  1842.                     break;
  1843.                 case 'punct_queryend':
  1844.                     if (($typearr[3] != 'comment_mysql') && ($typearr[3] != 'comment_ansi') && $typearr[3] != 'comment_c') {
  1845.                         $after     .= $html_line_break;
  1846.                         $after     .= $html_line_break;
  1847.                     }
  1848.                     $space_punct_listsep               = ' ';
  1849.                     $space_punct_listsep_function_name = ' ';
  1850.                     $space_alpha_reserved_word         = ' ';
  1851.                     $in_priv_list                      = FALSE;
  1852.                     break;
  1853.                 case 'comment_mysql':
  1854.                 case 'comment_ansi':
  1855.                     $after         .= $html_line_break;
  1856.                     break;
  1857.                 case 'punct':
  1858.                     $before         .= ' ';
  1859.                     // workaround for
  1860.                     // select * from mytable limit 0,-1
  1861.                     // (a side effect of this workaround is that
  1862.                     // select 20 - 9
  1863.                     // becomes
  1864.                     // select 20 -9
  1865.                     // )
  1866.                     if ($typearr[3] != 'digit_integer') {
  1867.                        $after        .= ' ';
  1868.                     }
  1869.                     break;
  1870.                 case 'punct_bracket_close_round':
  1871.                     $bracketlevel--;
  1872.                     if ($infunction == TRUE) {
  1873.                         $functionlevel--;
  1874.                         $after     .= ' ';
  1875.                         $before    .= ' ';
  1876.                     } else {
  1877.                         $indent--;
  1878.                         $before    .= ($mode != 'query_only' ? '</div>' : ' ');
  1879.                     }
  1880.                     $infunction    = ($functionlevel > 0) ? TRUE : FALSE;
  1881.                     break;
  1882.                 case 'alpha_columnType':
  1883.                     if ($typearr[3] == 'alpha_columnAttrib') {
  1884.                         $after     .= ' ';
  1885.                     }
  1886.                     if ($typearr[1] == 'alpha_columnType') {
  1887.                         $before    .= ' ';
  1888.                     }
  1889.                     break;
  1890.                 case 'alpha_columnAttrib':
  1891.  
  1892.                     // ALTER TABLE tbl_name AUTO_INCREMENT = 1
  1893.                     // COLLATE LATIN1_GENERAL_CI DEFAULT
  1894.                     if ($typearr[1] == 'alpha_identifier' || $typearr[1] == 'alpha_charset') {
  1895.                         $before .= ' ';
  1896.                     }
  1897.                     if (($typearr[3] == 'alpha_columnAttrib') || ($typearr[3] == 'quote_single') || ($typearr[3] == 'digit_integer')) {
  1898.                         $after     .= ' ';
  1899.                     }
  1900.                     // workaround for
  1901.                     // select * from mysql.user where binary user="root"
  1902.                     // binary is marked as alpha_columnAttrib
  1903.                     // but should be marked as a reserved word
  1904.                     if (strtoupper($arr[$i]['data']) == 'BINARY'
  1905.                       && $typearr[3] == 'alpha_identifier') {
  1906.                         $after     .= ' ';
  1907.                     }
  1908.                     break;
  1909.                 case 'alpha_reservedWord':
  1910.                     $arr[$i]['data'] = strtoupper($arr[$i]['data']);
  1911.                     if ((($typearr[1] != 'alpha_reservedWord')
  1912.                         || (($typearr[1] == 'alpha_reservedWord')
  1913.                             && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_no_newline, $keywords_no_newline_cnt)))
  1914.                         && ($typearr[1] != 'punct_level_plus')
  1915.                         && (!PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_no_newline, $keywords_no_newline_cnt))) {
  1916.                         // do not put a space before the first token, because
  1917.                         // we use a lot of eregi() checking for the first
  1918.                         // reserved word at beginning of query
  1919.                         // so do not put a newline before
  1920.                         //
  1921.                         // also we must not be inside a privilege list
  1922.                         if ($i > 0) {
  1923.                             // the alpha_identifier exception is there to
  1924.                             // catch cases like
  1925.                             // GRANT SELECT ON mydb.mytable TO myuser@localhost
  1926.                             // (else, we get mydb.mytableTO )
  1927.                             //
  1928.                             // the quote_single exception is there to
  1929.                             // catch cases like
  1930.                             // GRANT ... TO 'marc'@'domain.com' IDENTIFIED...
  1931.                             //
  1932.                             // TODO: fix all cases and find why this happens
  1933.  
  1934.                             if (!$in_priv_list || $typearr[1] == 'alpha_identifier' || $typearr[1] == 'quote_single' || $typearr[1] == 'white_newline') {
  1935.                                 $before    .= $space_alpha_reserved_word;
  1936.                             }
  1937.                         } else {
  1938.                         // on first keyword, check if it introduces a
  1939.                         // privilege list
  1940.                             if (PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_priv_list, $keywords_priv_list_cnt)) {
  1941.                                 $in_priv_list = TRUE;
  1942.                             }
  1943.                         }
  1944.                     } else {
  1945.                         $before    .= ' ';
  1946.                     }
  1947.  
  1948.                     switch ($arr[$i]['data']) {
  1949.                         case 'CREATE':
  1950.                             if (!$in_priv_list) {
  1951.                                 $space_punct_listsep       = $html_line_break;
  1952.                                 $space_alpha_reserved_word = ' ';
  1953.                             }
  1954.                             break;
  1955.                         case 'EXPLAIN':
  1956.                         case 'DESCRIBE':
  1957.                         case 'SET':
  1958.                         case 'ALTER':
  1959.                         case 'DELETE':
  1960.                         case 'SHOW':
  1961.                         case 'DROP':
  1962.                         case 'UPDATE':
  1963.                         case 'TRUNCATE':
  1964.                         case 'ANALYZE':
  1965.                         case 'ANALYSE':
  1966.                             if (!$in_priv_list) {
  1967.                                 $space_punct_listsep       = $html_line_break;
  1968.                                 $space_alpha_reserved_word = ' ';
  1969.                             }
  1970.                             break;
  1971.                         case 'INSERT':
  1972.                         case 'REPLACE':
  1973.                             if (!$in_priv_list) {
  1974.                                 $space_punct_listsep       = $html_line_break;
  1975.                                 $space_alpha_reserved_word = $html_line_break;
  1976.                             }
  1977.                             break;
  1978.                         case 'VALUES':
  1979.                             $space_punct_listsep       = ' ';
  1980.                             $space_alpha_reserved_word = $html_line_break;
  1981.                             break;
  1982.                         case 'SELECT':
  1983.                             $space_punct_listsep       = ' ';
  1984.                             $space_alpha_reserved_word = $html_line_break;
  1985.                             break;
  1986.                         default:
  1987.                             break;
  1988.                     } // end switch ($arr[$i]['data'])
  1989.  
  1990.                     $after         .= ' ';
  1991.                     break;
  1992.                 case 'digit_integer':
  1993.                 case 'digit_float':
  1994.                 case 'digit_hex':
  1995.                     //TODO: could there be other types preceding a digit?
  1996.                     if ($typearr[1] == 'alpha_reservedWord') {
  1997.                         $after .= ' ';
  1998.                     }
  1999.                     if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
  2000.                         $after     .= ' ';
  2001.                     }
  2002.                     if ($typearr[1] == 'alpha_columnAttrib') {
  2003.                         $before .= ' ';
  2004.                     }
  2005.                     break;
  2006.                 case 'alpha_variable':
  2007.                     // other workaround for a problem similar to the one
  2008.                     // explained below for quote_single
  2009.                     if (!$in_priv_list) {
  2010.                         $after      = ' ';
  2011.                     }
  2012.                     break;
  2013.                 case 'quote_double':
  2014.                 case 'quote_single':
  2015.                     // workaround: for the query
  2016.                     // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%'
  2017.                     // the @ is incorrectly marked as alpha_variable
  2018.                     // in the parser, and here, the '%' gets a blank before,
  2019.                     // which is a syntax error
  2020.                     if ($typearr[1] !='alpha_variable') {
  2021.                         $before        .= ' ';
  2022.                     }
  2023.                     if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
  2024.                         $after     .= ' ';
  2025.                     }
  2026.                     break;
  2027.                 case 'quote_backtick':
  2028.                     if ($typearr[3] != 'punct_qualifier') {
  2029.                         $after     .= ' ';
  2030.                     }
  2031.                     if ($typearr[1] != 'punct_qualifier') {
  2032.                         $before    .= ' ';
  2033.                     }
  2034.                     break;
  2035.                 default:
  2036.                     break;
  2037.             } // end switch ($typearr[2])
  2038.  
  2039. /*
  2040.             if ($typearr[3] != 'punct_qualifier') {
  2041.                 $after             .= ' ';
  2042.             }
  2043.             $after                 .= "\n";
  2044. */
  2045.             $str .= $before . ($mode=='color' ? PMA_SQP_formatHTML_colorize($arr[$i]) : $arr[$i]['data']). $after;
  2046.         } // end for
  2047.         if ($mode=='color') {
  2048.             $str .= '</span>';
  2049.         }
  2050.  
  2051.         return $str;
  2052.     } // end of the "PMA_SQP_formatHtml()" function
  2053. }
  2054.  
  2055. /**
  2056.  * Builds a CSS rule used for html formatted SQL queries
  2057.  *
  2058.  * @param  string  The class name
  2059.  * @param  string  The property name
  2060.  * @param  string  The property value
  2061.  *
  2062.  * @return string  The CSS rule
  2063.  *
  2064.  * @access public
  2065.  *
  2066.  * @see    PMA_SQP_buildCssData()
  2067.  */
  2068. function PMA_SQP_buildCssRule($classname, $property, $value)
  2069. {
  2070.     $str     = '.' . $classname . ' {';
  2071.     if ($value != '') {
  2072.         $str .= $property . ': ' . $value . ';';
  2073.     }
  2074.     $str     .= '}' . "\n";
  2075.  
  2076.     return $str;
  2077. } // end of the "PMA_SQP_buildCssRule()" function
  2078.  
  2079.  
  2080. /**
  2081.  * Builds CSS rules used for html formatted SQL queries
  2082.  *
  2083.  * @return string  The CSS rules set
  2084.  *
  2085.  * @access public
  2086.  *
  2087.  * @global array   The current PMA configuration
  2088.  *
  2089.  * @see    PMA_SQP_buildCssRule()
  2090.  */
  2091. function PMA_SQP_buildCssData()
  2092. {
  2093.     global $cfg;
  2094.  
  2095.     $css_string     = '';
  2096.     foreach ($cfg['SQP']['fmtColor'] AS $key => $col) {
  2097.         $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col);
  2098.     }
  2099.  
  2100.     for ($i = 0; $i < 8; $i++) {
  2101.         $css_string .= PMA_SQP_buildCssRule('syntax_indent' . $i, 'margin-left', ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']);
  2102.     }
  2103.  
  2104.     return $css_string;
  2105. } // end of the "PMA_SQP_buildCssData()" function
  2106.  
  2107. if ($is_minimum_common == FALSE) {
  2108.     /**
  2109.      * Gets SQL queries with no format
  2110.      *
  2111.      * @param  array   The SQL queries list
  2112.      *
  2113.      * @return string  The SQL queries with no format
  2114.      *
  2115.      * @access public
  2116.      */
  2117.     function PMA_SQP_formatNone($arr)
  2118.     {
  2119.         $formatted_sql = htmlspecialchars($arr['raw']);
  2120.         $formatted_sql = preg_replace("@((\015\012)|(\015)|(\012)){3,}@", "\n\n", $formatted_sql);
  2121.  
  2122.         return $formatted_sql;
  2123.     } // end of the "PMA_SQP_formatNone()" function
  2124.  
  2125.  
  2126.     /**
  2127.      * Gets SQL queries in text format
  2128.      *
  2129.      * @param  array   The SQL queries list
  2130.      *
  2131.      * @return string  The SQL queries in text format
  2132.      *
  2133.      * @access public
  2134.      */
  2135.     function PMA_SQP_formatText($arr)
  2136.     {
  2137.         /**
  2138.          * TODO WRITE THIS!
  2139.          */
  2140.          return PMA_SQP_formatNone($arr);
  2141.     } // end of the "PMA_SQP_formatText()" function
  2142. } // end if: minimal common.lib needed?
  2143.  
  2144. ?>
  2145.